import {
  Component,
  ViewEncapsulation,
  ChangeDetectionStrategy,
  Input,
  ElementRef,
  ChangeDetectorRef,
  Pipe,
  PipeTransform,
  OnDestroy,
  OnInit,
  AfterViewInit,
} from '@angular/core';
import { DomSanitizer, SafeHtml, SafeStyle, SafeScript, SafeUrl, SafeResourceUrl } from '@angular/platform-browser';
import { AnimationEvent } from '@angular/animations';
import { Subject } from 'rxjs';
import { TypeUtils } from '../core/utils/type';
import { MessageContainerService } from './message.container.service';
import { messageContent, messageWrapper } from './message-motion';
import { MessageOptions } from './interface';

@Pipe({
  name: 'safe',
})
export class SafePipe implements PipeTransform {
  constructor(protected dom: DomSanitizer) {}
  public transform(value, type): SafeHtml | SafeStyle | SafeScript | SafeUrl | SafeResourceUrl {
    switch (type) {
      case 'html':
        return this.dom.bypassSecurityTrustHtml(value);
      case 'style':
        return this.dom.bypassSecurityTrustStyle(value);
      case 'script':
        return this.dom.bypassSecurityTrustScript(value);
      case 'url':
        return this.dom.bypassSecurityTrustUrl(value);
      case 'resourceUrl':
        return this.dom.bypassSecurityTrustResourceUrl(value);
      default:
        return value;
    }
  }
}

@Component({
  selector: 'lv-message',
  exportAs: 'lvMessage',
  templateUrl: './message.component.html',
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  host: {
    '[style.maxWidth]': 'typeConvert(options.lvMaxWidth)',
    '[style.width]': 'typeConvert(options.lvWidth)',
    '[style.flex]': "'1 0 auto'",
  },
  animations: [messageWrapper, messageContent],
})
export class MessageComponent implements OnInit, OnDestroy, AfterViewInit {
  @Input() lvOptions;

  public animationState;
  public typeUtils = TypeUtils;
  public options: MessageOptions;
  public showState$ = new Subject<boolean>();
  public animStateMap = {
    topLeft: ['topIn', 'topOut'],
    topCenter: ['topIn', 'topOut'],
    topRight: ['rightIn', 'rightOut'],
  };

  public icons = {
    success: 'lv-icon-status-succeed',
    info: 'lv-icon-status-info',
    warning: 'lv-icon-status-warning-info',
    error: 'lv-icon-status-error',
  };

  private timer = null;

  constructor(
    public cdr: ChangeDetectorRef,
    public el: ElementRef,
    public messageContainer: MessageContainerService,
  ) {}

  get contentMinWidth(): string {
    return this.isIE() ? '100%' : this._getWidth();
  }

  /**
   * Fixed issue #219 in IE 11/edge
   */
  get contentWidth(): string {
    return this.options?.lvWidth ? this._getWidth() : null;
  }

  get contentMaxWidth(): string {
    return this.options?.lvMaxWidth ? this._getWidth() : null;
  }

  mouseLeave(): void {
    this.startTiming();
  }

  mouseEnter(): void {
    if (this.options.lvKeepShowOnHover) {
      this.clearTimer();
    }
  }

  ngAfterViewInit(): void {
    this.lvOptions.options.lvOnShow?.();
  }

  ngOnDestroy(): void {
    this.clearTimer();
    if (this.lvOptions.options.lvOnDestroy) {
      this.lvOptions.options.lvOnDestroy();
    }
  }

  public typeConvert(value): string {
    if (typeof value === 'number') {
      return `${value}px`;
    } else if (typeof value === 'string') {
      return value;
    } else {
      return 'auto';
    }
  }

  update(): void {
    this.options = this.lvOptions.options;
    this.el.nativeElement.style.transform = `translate(${this.options.lvOffset.x}px, ${this.options.lvOffset.y}px)`;
    setTimeout((): void => {
      this.setAnimationState('show');
      this.startTiming();
      this.cdr.markForCheck();
    }, 0);
  }

  close(): void {
    this.setAnimationState('hide');
    this.cdr.markForCheck();
    const opensubscribe = this.showState$.subscribe((show): void => {
      if (!show) {
        opensubscribe.unsubscribe();
        this.messageContainer.remove(this.cdr, this.options.lvPosition, this.lvOptions.id);
      }
    });
  }

  ngOnInit(): void {
    this.options = this.lvOptions.options;
    this.el.nativeElement.style.transform = `translate(${this.options.lvOffset.x}px, ${this.options.lvOffset.y}px)`;
    this.lvOptions.change$.subscribe((): void => {
      this.update();
    });

    // Initialize animatin status
    this.setAnimationState('hide');
    setTimeout((): void => {
      this.setAnimationState('show');
      this.startTiming();
      this.cdr.markForCheck();
    }, 0);
  }

  /**
   * listen animation(done)
   */
  public onAnimationDone(event: AnimationEvent): void {
    const flag1 =
      event.triggerName === 'messageWrapper' &&
      event.fromState !== 'void' &&
      (event.toState === 'topIn' || event.toState === 'leftIn' || event.toState === 'rightIn');
    const flag2 =
      event.triggerName === 'messageWrapper' &&
      ((event.fromState === 'topIn' && event.toState === 'topOut') ||
        (event.fromState === 'leftIn' && event.toState === 'leftOut') ||
        (event.fromState === 'rightIn' && event.toState === 'rightOut'));
    if (flag1) {
      this.showState$.next(true);
    }
    if (flag2) {
      this.showState$.next(false);
    }
  }

  private clearTimer(): void {
    if (this.timer !== null) {
      clearTimeout(this.timer);
      this.timer = null;
    }
  }

  private startTiming(): void {
    const dur = this.options.lvDuration;
    if (dur === 0) {
      return;
    }

    this.timer = setTimeout((): void => {
      this.close();
    }, dur);
  }

  private setAnimationState(_state: string): void {
    if (_state === 'show') {
      this.animationState = this.animStateMap[this.lvOptions.options.lvPosition][0];
    } else if (_state === 'hide') {
      this.animationState = this.animStateMap[this.lvOptions.options.lvPosition][1];
    }
  }

  private _getWidth(): string {
    if (this.lvOptions.isSimple) {
      if (this.options.lvType && this.options.lvType !== 'default' && this.options.lvShowIcon) {
        return 'calc(100% - 24px - 32px)';
      } else {
        return 'calc(100% - 24px)';
      }
    }
    return undefined;
  }

  private isIE(): boolean {
    const userAgent = navigator.userAgent.toLowerCase();
    const flag =
      userAgent.indexOf('msie') !== -1 ||
      userAgent.indexOf('trident') !== -1 ||
      userAgent.indexOf('edge') !== -1 ||
      !!(window as any).ActiveXObject ||
      'ActiveXObject' in window;
    if (flag) {
      return true;
    } else {
      return false;
    }
  }
}
